/** @file         xxx_device.c
 *  @brief        设备文件
 *  @details      基于驱动模块的方式实现 设备注册/注销等等操作。
 *  @author       lzm
 *  @date         2021-03-21 10:20:03
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *
 **********************************************************
 *  @LOG 修改日志:
 **********************************************************
*/


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

/* 
 * 步骤注释声明
 * [module]：模块框架
 * [bus_dev]：总线设备框架。
 * [bus_drv]：总线驱动框架。
 * 总线设备框架和总线驱动框架都是属于内核的架子，和模块框架一个性质。 
 */

/* [bus_dev][1] */
/* 声明外部总线 */
extern struct bus_type lzm_bus;

/* [dev][2] */
/* 实现设备结构体内容 */
/** @brief  xxx_dev_release 该函数在设备被注销时执行
  * @param  
  * @retval
  * @author lzm
  */
void xxx_dev_release(struct device *dev)
{
    printk("%s-%s\n", __FILE__, __func__); // 打印文件名及函数
}

/* [bus_dev][3] */
/* 填充设备结构体 */
static struct device xxx_dev =
{
	.init_name = "xxx_dev",
	.bus = &lzm_bus,
	.release = xxx_dev_release,
};

/* [bus_dev][4] */
/* 实现属性文件所需内容 */

/* 实现 show 回调函数，让用户能通过 cat 命令获取该设备资源 id */
/** @brief  xxx_dev_show
  * @param  
  * @retval 
  * @author lzm
  */
unsigned long id = 0; /* 资源 id */
ssize_t xxx_dev_id_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "%ld\n", id); // 拷贝到 buf 中
}

/* 实现 store 回调函数，对于 echo 命令。 */
/** @brief  xxx_dev_store
  * @param  
  * @retval 
  * @author lzm
  */
ssize_t xxx_dev_id_store(struct device * dev, struct device_attribute * attr, const char *buf, size_t count)
{
	(void)kstrtoul(buf, 10, &id); // 把 buf 字符串按 10 进制转换为整数保存到 id。
	return count;
}

// 设置该文件 名称、属性等等
DEVICE_ATTR(dev_id, S_IRUSR | S_IWUSR, xxx_dev_id_show, xxx_dev_id_store); // dev_id 为 device 属性文件名称。在 /sys/bus/<bus-name>/devices 下 实际文件在 /sys/devices 下


/* [module][1] */
/** @brief   xxx_dev_init
  * @details dev module 入口函数
  * @param  
  * @retval 
  * @author lzm
  */
static int __init xxx_dev_init(void)
{
    printk("xxx_dev init!\n");
    
    /* [bus_dev][5] 注册设备 */
    (void)device_register(&xxx_dev);

    /* [bus_dev][6] 创建属性文件*/
    (void)device_create_file(&xxx_dev, &dev_attr_dev_id); // 创建后会在 /sys/bus/<bus-name>/devices 下出现该文件链接。实际文件在 /sys/devices。dev_attr_dev_id 参考 DEVICE_ARRT 源码。bus_attr_##name。

    return 1;
}
module_init(xxx_dev_init);

/* [module][2] */
/** @brief   xxx_dev_exit
  * @details dev module 出口函数
  * @param  
  * @retval 
  * @author lzm
  */
static void __exit xxx_dev_exit(void)
{
    printk("xxx_dev exit!\n");

    /* [bus_dev][7] 删除属性文件 */
    device_remove_file(&xxx_dev, &dev_attr_dev_id);

    /* [bus_dev][8] 注销设备 */
    device_unregister(&xxx_dev);
}
module_exit(xxx_dev_exit);

/* [module][3] */
MODULE_AUTHOR("lizhuming");
MODULE_LICENSE("GPL");

